package cn.monkey.family.server.web.method;

import cn.monkey.family.data.uc.auth.UserSession;
import cn.monkey.family.data.context.Context;
import cn.monkey.family.data.context.DefaultRequestContext;
import cn.monkey.family.data.context.RequestContext;
import com.google.common.base.Strings;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpStatus;
import org.springframework.lang.NonNull;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import java.nio.charset.Charset;

public class RequestContextArgumentResolver implements HandlerMethodArgumentResolver {
    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return RequestContext.class.isAssignableFrom(parameter.getParameterType())
                && parameter.hasParameterAnnotation(Context.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer,
                                  @NonNull NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception {
        Context context = parameter.getParameterAnnotation(Context.class);
        if (context == null) {
            // NEVER HAPPENED HERE
            throw new IllegalArgumentException("invalid parameter: " + parameter);
        }
        HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
        if (servletRequest == null) {
            throw HttpClientErrorException.create(HttpStatus.INTERNAL_SERVER_ERROR, "request is null", null, null, Charset.defaultCharset());
        }
        Context.Option authorization = context.authorization();
        UserSession session = this.tryResolverUserSession(authorization, servletRequest);
        DefaultRequestContext.DefaultRequestContextBuilder builder = DefaultRequestContext.builder();
        return builder.userSession(session)
                .orgId(this.tryResolveFromRequestHeader(context.orgId(), servletRequest))
                .token(this.tryResolveFromRequestHeader(context.authorization(), servletRequest))
                .platformId(this.tryResolveFromRequestHeader(context.platformId(), servletRequest))
                .traceId(this.tryResolveFromRequestHeader(context.traceId(), servletRequest))
                .build();
    }

    private String tryResolveFromRequestHeader(Context.Option option, HttpServletRequest servletRequest) {
        String value = servletRequest.getHeader(option.value());
        if (option.required() && Strings.isNullOrEmpty(option.value())) {
            throw new IllegalArgumentException("http header: [" + value + "] is not exists");
        }
        return value;
    }

    private UserSession tryResolverUserSession(Context.Option authorization,
                                               HttpServletRequest request) {
        boolean required = authorization.required();
        UserSession userSession = (UserSession) request.getAttribute(UserSession.KEY);
        if (required && userSession == null) {
            throw HttpClientErrorException.create(HttpStatus.UNAUTHORIZED, "user session is required", null, null, Charset.defaultCharset());
        }
        return userSession;
    }
}
